﻿namespace Microsoft.VisualStudio.ServiceModel.DomainServices.Tools
{
    using Microsoft.ServiceModel.DomainServices.Tools;
    using System;
    using System.CodeDom;
    using System.Data;
    using System.Data.Mapping;
    using System.Data.Metadata.Edm;
    using System.Data.Objects;
    using System.Globalization;
    using System.Reflection;
    using System.ServiceModel.DomainServices;

    internal class LinqToEntitiesDbContext : LinqToEntitiesContextBase
    {
        private bool? _isCodeFirstModel;
        private System.Data.Metadata.Edm.MetadataWorkspace _metadataWorkspace;

        public LinqToEntitiesDbContext(Type contextType) : base(contextType)
        {
            this._isCodeFirstModel = null;
        }

        protected override CodeTypeDeclaration CreateBusinessLogicClass(CodeGenContext codeGenContext, CodeNamespace codeNamespace, string className)
        {
            foreach (string str in BusinessLogicClassConstants.LinqToEntitiesDbImports)
            {
                codeNamespace.Imports.Add(new CodeNamespaceImport(str));
            }
            if (base.ContextType.Namespace != codeNamespace.Name)
            {
                codeNamespace.Imports.Add(new CodeNamespaceImport("System.ServiceModel.DomainServices.EntityFramework"));
                codeNamespace.Imports.Add(new CodeNamespaceImport(base.ContextType.Namespace));
            }
            codeGenContext.AddReference(typeof(EntityState).Assembly.FullName);
            codeGenContext.AddReference("Microsoft.ServiceModel.DomainServices.EntityFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");
            if (DbContextUtilities.DbContextTypeReference != null)
            {
                codeGenContext.AddReference(DbContextUtilities.DbContextTypeReference.Assembly.FullName);
            }
            CodeTypeDeclaration declaration = CodeGenUtilities.CreateTypeDeclaration(className, codeNamespace.Name);
            CodeTypeReference reference = new CodeTypeReference(BusinessLogicClassConstants.DbDomainServiceTypeName, new CodeTypeReference[] { new CodeTypeReference(base.ContextType.Name) });
            declaration.BaseTypes.Add(reference);
            return declaration;
        }

        protected override void GenerateDeleteMethod(CodeGenContext codeGenContext, CodeTypeDeclaration businessLogicClass, BusinessLogicEntity entity)
        {
            string name = CodeGenUtilities.MakeLegalParameterName(entity.Name);
            CodeMemberMethod method = new CodeMemberMethod();
            businessLogicClass.Members.Add(method);
            LinqToEntitiesEntity efDbEntity = (LinqToEntitiesEntity) entity;
            method.Name = "Delete" + CodeGenUtilities.MakeLegalEntityName(entity.Name);
            method.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            method.Parameters.Add(new CodeParameterDeclarationExpression(entity.ClrType.Name, name));
            CodeArgumentReferenceExpression expression = new CodeArgumentReferenceExpression(name);
            CodeExpression contextReferenceExpression = GetContextReferenceExpression();
            CodeVariableDeclarationStatement statement = new CodeVariableDeclarationStatement(new CodeTypeReference("DbEntityEntry", new CodeTypeReference[] { new CodeTypeReference(entity.ClrType.Name) }), "entityEntry", new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(contextReferenceExpression, "Entry"), new CodeExpression[] { expression }));
            method.Statements.Add(statement);
            CodeVariableReferenceExpression targetObject = new CodeVariableReferenceExpression("entityEntry");
            CodePropertyReferenceExpression left = new CodePropertyReferenceExpression(targetObject, "State");
            CodeFieldReferenceExpression right = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(EntityState).Name), Enum.GetName(typeof(EntityState), EntityState.Deleted));
            CodeExpression condition = CodeGenUtilities.MakeNotEqual(typeof(EntityState), left, right, codeGenContext.IsCSharp);
            CodeFieldReferenceExpression expression7 = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(EntityState).Name), Enum.GetName(typeof(EntityState), EntityState.Deleted));
            CodeAssignStatement statement2 = new CodeAssignStatement(left, expression7);
            CodeMethodInvokeExpression expression8 = new CodeMethodInvokeExpression(GetDbSetReferenceExpression(efDbEntity), "Attach", new CodeExpression[] { expression });
            CodeMethodInvokeExpression expression9 = new CodeMethodInvokeExpression(GetDbSetReferenceExpression(efDbEntity), "Remove", new CodeExpression[] { expression });
            CodeConditionStatement statement3 = new CodeConditionStatement(condition, new CodeStatement[] { statement2 }, new CodeStatement[] { new CodeExpressionStatement(expression8), new CodeExpressionStatement(expression9) });
            method.Statements.Add(statement3);
        }

        protected override void GenerateInsertMethod(CodeGenContext codeGenContext, CodeTypeDeclaration businessLogicClass, BusinessLogicEntity entity)
        {
            CodeMemberMethod method = new CodeMemberMethod();
            businessLogicClass.Members.Add(method);
            string name = CodeGenUtilities.MakeLegalParameterName(entity.Name);
            LinqToEntitiesEntity efDbEntity = (LinqToEntitiesEntity) entity;
            method.Name = "Insert" + CodeGenUtilities.MakeLegalEntityName(entity.Name);
            method.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            method.Parameters.Add(new CodeParameterDeclarationExpression(entity.ClrType.Name, name));
            CodeArgumentReferenceExpression expression = new CodeArgumentReferenceExpression(name);
            CodeExpression contextReferenceExpression = GetContextReferenceExpression();
            CodeVariableDeclarationStatement statement = new CodeVariableDeclarationStatement(new CodeTypeReference("DbEntityEntry", new CodeTypeReference[] { new CodeTypeReference(entity.ClrType.Name) }), "entityEntry", new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(contextReferenceExpression, "Entry"), new CodeExpression[] { expression }));
            method.Statements.Add(statement);
            CodeVariableReferenceExpression targetObject = new CodeVariableReferenceExpression("entityEntry");
            CodePropertyReferenceExpression left = new CodePropertyReferenceExpression(targetObject, "State");
            CodeFieldReferenceExpression right = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(EntityState).Name), Enum.GetName(typeof(EntityState), EntityState.Detached));
            CodeExpression condition = CodeGenUtilities.MakeNotEqual(typeof(EntityState), left, right, codeGenContext.IsCSharp);
            CodeFieldReferenceExpression expression7 = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(EntityState).Name), Enum.GetName(typeof(EntityState), EntityState.Added));
            CodeAssignStatement statement2 = new CodeAssignStatement(left, expression7);
            CodeMethodInvokeExpression expression8 = new CodeMethodInvokeExpression(GetDbSetReferenceExpression(efDbEntity), "Add", new CodeExpression[] { expression });
            CodeConditionStatement statement3 = new CodeConditionStatement(condition, new CodeStatement[] { statement2 }, new CodeStatement[] { new CodeExpressionStatement(expression8) });
            method.Statements.Add(statement3);
        }

        protected override CodeMemberMethod GenerateSelectMethod(CodeGenContext codeGenContext, CodeTypeDeclaration businessLogicClass, BusinessLogicEntity entity)
        {
            CodeMemberMethod method = null;
            LinqToEntitiesEntity entity2 = entity as LinqToEntitiesEntity;
            if ((entity2 != null) && (entity2.DefaultObjectSetName != null))
            {
                method = new CodeMemberMethod();
                businessLogicClass.Members.Add(method);
                method.Comments.Add(new CodeCommentStatement(Resources.BusinessLogicClass_Query_Method_Remarks, false));
                string text = string.Format(CultureInfo.CurrentCulture, Resources.BusinessLogicClass_Query_Method_EF_Remarks, new object[] { entity2.DefaultObjectSetName });
                method.Comments.Add(new CodeCommentStatement(text, false));
                method.Name = "Get" + CodeGenUtilities.MakeLegalEntityName(entity2.DefaultObjectSetName);
                method.ReturnType = new CodeTypeReference("IQueryable", new CodeTypeReference[] { new CodeTypeReference(entity.Name) });
                method.Attributes = MemberAttributes.Public | MemberAttributes.Final;
                CodeExpression expression = new CodePropertyReferenceExpression(GetContextReferenceExpression(), entity2.DefaultObjectSetName);
                CodeMethodReturnStatement statement = new CodeMethodReturnStatement(expression);
                method.Statements.Add(statement);
            }
            return method;
        }

        protected override void GenerateUpdateMethod(CodeGenContext codeGenContext, CodeTypeDeclaration businessLogicClass, BusinessLogicEntity entity)
        {
            string name = "current" + entity.ClrType.Name;
            CodeMemberMethod method = new CodeMemberMethod();
            businessLogicClass.Members.Add(method);
            method.Name = "Update" + CodeGenUtilities.MakeLegalEntityName(entity.Name);
            method.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            method.Parameters.Add(new CodeParameterDeclarationExpression(entity.ClrType.Name, name));
            LinqToEntitiesEntity entity2 = (LinqToEntitiesEntity) entity;
            if (!entity2.HasTimestampMember)
            {
                CodeExpression targetObject = new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), "ChangeSet");
                CodeMethodReferenceExpression expression2 = new CodeMethodReferenceExpression(targetObject, "GetOriginal");
                CodeMethodInvokeExpression expression3 = new CodeMethodInvokeExpression(expression2, new CodeExpression[] { new CodeArgumentReferenceExpression(name) });
                CodeExpression expression4 = new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), "DbContext");
                CodePropertyReferenceExpression expression5 = new CodePropertyReferenceExpression(expression4, entity2.DefaultObjectSetName);
                CodeMethodInvokeExpression expression = new CodeMethodInvokeExpression(expression5, "AttachAsModified", new CodeExpression[] { new CodeArgumentReferenceExpression(name), expression3, new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), "DbContext") });
                CodeExpressionStatement statement = new CodeExpressionStatement(expression);
                method.Statements.Add(statement);
            }
            else
            {
                CodeExpression expression7 = new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), "DbContext");
                CodePropertyReferenceExpression expression8 = new CodePropertyReferenceExpression(expression7, entity2.DefaultObjectSetName);
                CodeMethodInvokeExpression expression9 = new CodeMethodInvokeExpression(expression8, "AttachAsModified", new CodeExpression[] { new CodeArgumentReferenceExpression(name), new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), "DbContext") });
                CodeExpressionStatement statement2 = new CodeExpressionStatement(expression9);
                method.Statements.Add(statement2);
            }
        }

        private static CodeExpression GetContextReferenceExpression()
        {
            return new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), "DbContext");
        }

        private static CodeExpression GetDbSetReferenceExpression(LinqToEntitiesEntity efDbEntity)
        {
            return new CodePropertyReferenceExpression(GetContextReferenceExpression(), efDbEntity.DefaultObjectSetName);
        }

        private void InitMetadataWorkspace()
        {
            if (!this._isCodeFirstModel.HasValue)
            {
                this._isCodeFirstModel = new bool?(!this.TryInitMetadataWorkspaceFromResources());
            }
            if (this._isCodeFirstModel.Value)
            {
                DbContextUtilities.SetDbInitializer(base.ContextType, DbContextUtilities.DbContextTypeReference, null);
                if (base.ContextType.GetConstructor(Type.EmptyTypes) == null)
                {
                    throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resources.LinqToEntitiesDbContext_DefaultCtorNotFound, new object[] { base.ContextType.FullName }));
                }
                try
                {
                    object obj2 = Activator.CreateInstance(base.ContextType);
                    if (obj2 != null)
                    {
                        Type type2 = DbContextUtilities.LoadTypeFromAssembly(DbContextUtilities.GetDbContextTypeReference(base.ContextType).Assembly, "System.Data.Entity.Infrastructure.IObjectContextAdapter");
                        if (type2 != null)
                        {
                            PropertyInfo property = type2.GetProperty("ObjectContext");
                            if (property != null)
                            {
                                ObjectContext context = property.GetValue(obj2, null) as ObjectContext;
                                if (context != null)
                                {
                                    this._metadataWorkspace = context.MetadataWorkspace;
                                }
                            }
                        }
                    }
                }
                catch (TargetInvocationException exception)
                {
                    throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resources.LinqToEntitiesDbContext_UnableToCreateContext, new object[0]), (exception == null) ? null : exception.InnerException);
                }
                catch (Exception exception2)
                {
                    if (exception2.IsFatal())
                    {
                        throw;
                    }
                    throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resources.LinqToEntitiesDbContext_UnableToCreateContext, new object[0]), exception2);
                }
            }
            this._metadataWorkspace.LoadFromAssembly(base.ContextType.Assembly);
        }

        private bool TryInitMetadataWorkspaceFromResources()
        {
            this._metadataWorkspace = System.Data.Mapping.MetadataWorkspaceUtilities.CreateMetadataWorkspaceFromResources(base.ContextType, DbContextUtilities.DbContextTypeReference);
            return (this._metadataWorkspace != null);
        }

        public override System.Data.Metadata.Edm.MetadataWorkspace MetadataWorkspace
        {
            get
            {
                if (this._metadataWorkspace == null)
                {
                    this.InitMetadataWorkspace();
                }
                return this._metadataWorkspace;
            }
        }

        internal override bool NeedToGenerateMetadataClasses
        {
            get
            {
                if (!this._isCodeFirstModel.HasValue)
                {
                    this._isCodeFirstModel = new bool?(!this.TryInitMetadataWorkspaceFromResources());
                }
                return !this._isCodeFirstModel.Value;
            }
        }
    }
}

